package com.allwees.bs.module.pms.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.allwees.bs.core.model.constant.CacheName;

import com.allwees.bs.core.model.product.entity.SkuModel;
import com.allwees.bs.module.base.service.impl.BoServiceImpl;
import com.allwees.bs.module.mms.service.StoreService;
import com.allwees.bs.module.pms.dao.CategoryDao;
import com.allwees.bs.module.pms.dao.ProductDao;
import com.allwees.bs.module.pms.dao.SkuDao;
import com.allwees.bs.module.pms.dto.ProductDto;
import com.allwees.bs.module.pms.entity.Category;
import com.allwees.bs.module.pms.entity.Product;
import com.allwees.bs.module.pms.entity.Sku;
import com.allwees.bs.module.pms.req.PutoffProductReq;
import com.allwees.bs.module.pms.req.PutonProductReq;
import com.allwees.bs.module.pms.req.SetCategoryReq;
import com.allwees.bs.module.pms.req.query.ProductsSkusQueryReq;
import com.allwees.bs.module.pms.service.BaseProductService;
import com.allwees.bs.module.pms.service.CategoryService;
import com.allwees.bs.module.pms.service.ProductOnoffLogService;
import com.allwees.bs.module.pms.service.SkuService;
import com.allwees.bs.module.pms.vo.ExportProductSkuBean;
import com.allwees.core.cache.RedisUtil;
import com.allwees.core.common.constant.EStatus;
import com.allwees.core.common.util.DateUtil;
import com.allwees.core.common.util.MoneyUtil;

import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.function.BinaryOperator;

/**
 * @author yangylsky
 * @since 2019-08-21
 */
@Service("baseProductService")
public class BaseProductServiceImpl extends BoServiceImpl<ProductDao, Product> implements BaseProductService {

    @Resource
    protected CategoryDao categoryDao;

    @Autowired
    protected CategoryService categoryService;

    @Autowired
    protected ProductOnoffLogService productOnoffLogService;

    @Resource
    protected ProductDao productDao;

    @Resource
    protected SkuDao skuDao;

    @Autowired
    protected SkuService skuService;

    @Autowired
    protected StoreService storeService;

    @Override
    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
    public String genProductNo(Category category) {
        String productSeq = category.nextProductSeq();
        category.setProductSeq(productSeq);
        categoryDao.updateSeq(category.getId(), productSeq);
        return category.getNo() + "-" + productSeq;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int puton(PutonProductReq req) {
        List<Product> products = findByUuids(req.getProductUuids());
        int succcessNum = 0;
        String operatorUuid = req.getBranchUserUuid();
        for (Product product : products) {
            if (puton(product, operatorUuid)) {
                succcessNum++;
                ProductDto productDto = ProductDto.of(product);
                productDto.setOperatorUuid(operatorUuid);
                //context.publishEvent(new ProductPutonEvent(productDto));
                productOnoffLogService.logPuton(productDto);
//                int skuCount = skuDao.getSkuCount(product.getUuid());
//                ProdActionUtil.logPutOnAction(productDto,skuCount);
            }
        }
        return succcessNum;
    }

    protected boolean puton(Product product, String operatorUuid) {
        return false;
    }

    /**
     * 1.A端上架
     * 2.B端上架
     * 3.批量更新发布字段信息（publishAt, score, retailPrice, originalPrice等）
     * @param product
     * @return
     */
    protected void setPublishInfo(Product product) {
        boolean changed = false;
        product.assertStatus(EStatus.NORMAL);

        //更新publishAt
        if (product.getPublishAt() == null) {
            product.setPublishAt(DateUtil.now());
            changed = true;
        }

        List<Sku> skus = skuService.lambdaQuery().eq(SkuModel::getProductUuid, product.getUuid()).list();
        if (updateRetailPrice(product, skus)) {
            changed = true;
        }

        //清除C端对应Product缓存
        if (changed) {
            RedisUtil.del(
                    CacheName.PROD_PRODUCT_BY_UUID + "::" + product.getUuid(),
                    CacheName.PROD_PRODUCT_DETAILS + "::" + product.getUuid()
            );
        }
    }

    /**
     * 获取最小售价的sku
     * spu的售价，原价，运费和其保持一致
     * @param product
     * @param skus
     * @param <S>
     * @return
     */
    protected <S extends Sku> boolean updateRetailPrice(Product product, List<S> skus) {
        Assert.isTrue(CollectionUtils.isNotEmpty(skus),"请先上传一些Sku~");
        BigDecimal productRetailPrice = product.getRetailPrice();
        BigDecimal productOriginalPrice = product.getOriginalPrice();
        BigDecimal productShippingPrice = product.getShippingPrice();
        BinaryOperator<S> operator = BinaryOperator.minBy(Comparator.comparing(Sku::getRetailPrice));
        Sku skuWithMinRetailPrice = skus.stream().reduce(operator).get();
        BigDecimal minSkuRetailPrice = skuWithMinRetailPrice.getRetailPrice();
        boolean retailPriceChanged = !MoneyUtil.equals(productRetailPrice, minSkuRetailPrice);
        if(retailPriceChanged){
            product.setRetailPrice(minSkuRetailPrice);
        }
        BigDecimal minSkuOriginalPrice = skuWithMinRetailPrice.getOriginalPrice();
        boolean originalPriceChanged = !MoneyUtil.equals(productOriginalPrice, minSkuOriginalPrice);
        if(originalPriceChanged){
            product.setOriginalPrice(minSkuOriginalPrice);
        }
        BigDecimal minSkuShippingPrice = skuWithMinRetailPrice.getShippingPrice();
        boolean shippingPriceChanged = !MoneyUtil.equals(productShippingPrice, minSkuShippingPrice);
        if(shippingPriceChanged){
            product.setShippingPrice(minSkuShippingPrice);
        }
        return retailPriceChanged || originalPriceChanged || shippingPriceChanged;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int putoff(PutoffProductReq req) {
        List<Product> products = findByUuids(req.getProductUuids());
        int succcessNum = 0;
        for (Product product : products) {
            if (putoff(product)) {
                succcessNum++;
                ProductDto productDto = ProductDto.of(product);
                productDto.setOperatorUuid(req.getBranchUserUuid());
                productDto.setReason(req.getReason());
                productDto.setRemark(req.getRemark());
                //context.publishEvent(new ProductPutoffEvent(productDto));
                RedisUtil.del(CacheName.PROD_PRODUCT_DETAILS + "::" + product.getUuid());
                productOnoffLogService.logPutoff(productDto);
//                int skuCount = skuDao.getSkuCount(product.getUuid());
//                ProdActionUtil.logPutOffAction(productDto,skuCount);
            }
        }
        return succcessNum;
    }

    @Override
    public boolean batchUpdateCategory(SetCategoryReq req) {
        return this.lambdaUpdate().in(Product::getUuid, req.getProductUuids()).set(Product::getCategoryNo, req.getCategoryNo()).update();
    }

    protected boolean putoff(Product product) {
        return false;
    }

    @Override
    public List<ExportProductSkuBean> exportProductsSkus(Page<ExportProductSkuBean> page, ProductsSkusQueryReq req) {
        return skuDao.queryProductsSkus(page, req);
    }


    @Override
    public boolean updateBatchById(Collection<Product> entityList){
        return super.updateBatchById(entityList);
    }
    //@CachePut(cacheNames = CacheName.PROD_PRODUCT_MODEL_BY_UUID, key = "#productUuid", unless = "#result == null")
    public void batchUpdateNumLimitReq(String productUuid, Integer num) {
        productDao.updateNumPerUser(num,productUuid);
        RedisUtil.hset(CacheName.PROD_PRODUCT_MODEL_BY_UUID,productUuid,num);

    }


}
